#include "spl.includes.h"
#include "splib.h"

using namespace SPLib;
using namespace SPLib::Windows;
using namespace SPLib::Geom;

#pragma region Management
LRESULT WrapWnd::destroy()
{
  // check if window exists
  if (IsWindow(mHandle))
  {
    // check if we are calling from the window thread
    if (GetWindowThreadProcessId(mHandle, NULL) != GetCurrentThreadId())
    {
      // no, send a close message
      return SendMessage(mHandle, WM_CLOSE, TRUE, 0);
    }
    else
    {
      // destroy window
      return DestroyWindow(mHandle);
    }
  }

  // operation failed
  return FALSE;
}

WPARAM WrapWnd::run(BOOL fAllWindows)
{
  // message struct
  MSG msg;
  msg.wParam = NULL;
  // message source
  HWND hWnd = fAllWindows ? NULL : mHandle;

  // dispatch every message
  while (GetMessage(&msg, hWnd, 0, 0) > 0)
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  // return last message (should be WM_QUIT) wParam
  return msg.wParam;
}
#pragma endregion

#pragma region Notify Icon
BOOL WrapWnd::createNotifyIcon(HICON hIcon, PCTSTR szTip, UINT uID)
{
  NOTIFYICONDATA iconData;

  // set notify icon data
  iconData.cbSize = NOTIFYICONDATA_V1_SIZE;
  iconData.hWnd = mHandle;
  iconData.uID = uID;
  iconData.uCallbackMessage = WM_NOTIFYICON;
  iconData.hIcon = hIcon;
  iconData.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
  iconData.uVersion = NOTIFYICON_VERSION;

  // copy tooltip message
  _tcsncpy(iconData.szTip, szTip, _countof(iconData.szTip));

  // add notify icon to the tray area
  return Shell_NotifyIcon(NIM_ADD, &iconData) == TRUE;
}

BOOL WrapWnd::destroyNotifyIcon(UINT uID)
{
  NOTIFYICONDATA iconData;

  // set notify icon data
  iconData.cbSize = NOTIFYICONDATA_V1_SIZE;
  iconData.hWnd = mHandle; /* Wooh, mistery, but this must be set */
  iconData.uID = uID;
  iconData.uFlags = 0;

  // remove the notify icon from the tray area
  return Shell_NotifyIcon(NIM_DELETE, &iconData) == TRUE;
}

BOOL WrapWnd::notify(
  PCTSTR szInfo,
  PCTSTR szTitle,
  UINT uID,
  DWORD dwInfoFlags,
  HICON hIcon
  )
{
  NOTIFYICONDATA iconData;

  // set notification data
  iconData.cbSize = NOTIFYICONDATA_V1_SIZE;
  iconData.hWnd = mHandle; /* Wooh, mistery, but this must be set */
  iconData.uID = uID;
  iconData.hIcon = hIcon;
  iconData.uFlags = NIF_INFO;
  iconData.dwInfoFlags = dwInfoFlags;
  // deprecated since Windows Vista, use only for compatibility
  iconData.uTimeout = 2500;

  // set balloon text and title
  _tcsncpy(iconData.szInfo, szInfo, _countof(iconData.szInfo));
  _tcsncpy(iconData.szInfoTitle, szTitle, _countof(iconData.szInfoTitle));

  // show balloon
  return Shell_NotifyIcon(NIM_MODIFY, &iconData) == TRUE;
}
#pragma endregion

#pragma region Scroll
VOID WrapWnd::setScrollX(INT value, ScrollInfo& scroll)
{
  Geom::Rect wndRect = getClient();

  value = max(0, value);
  value = min(scroll.max.x, value);

  // If the current position does not change, do not scroll.
  if (value == scroll.pos.x) return;

  // Reset the current scroll position.
  scroll.pos.x = value;
  invalidate(FALSE);

  // Reset the scroll bar.
  SCROLLINFO si;
  si.cbSize = sizeof(si);
  si.fMask  = SIF_POS;
  si.nPos   = scroll.pos.x;
  SetScrollInfo(*this, SB_HORZ, &si, TRUE);
}

VOID WrapWnd::setScrollY(INT value, ScrollInfo& scroll)
{
  Geom::Rect wndRect = getClient();

  value = max(0, value);
  value = min(scroll.max.y, value);

  // If the current position does not change, do not scroll.
  if (value == scroll.pos.y) return;

  // Reset the current scroll position.
  scroll.pos.y = value;
  invalidate(FALSE);

  // Reset the scroll bar.
  SCROLLINFO si;
  si.cbSize = sizeof(si);
  si.fMask  = SIF_POS;
  si.nPos   = scroll.pos.y;
  SetScrollInfo(*this, SB_VERT, &si, TRUE);
}

VOID WrapWnd::updateScroll(const SIZE& content, ScrollInfo& scroll)
{
  // surface bounds
  Geom::Rect rect = getClient();
  // scroll maximum
  Geom::Size maxScroll;

  if (!content.cx || !content.cy)
  {
    // reset scroll values
    scroll.pos.x = scroll.pos.y = 0;
    scroll.max.x = scroll.max.y = 0;
    maxScroll.setEmpty();
  }
  else
  {
    // maximum scroll values
    maxScroll.cx = content.cx;
    maxScroll.cy = content.cy;
    scroll.max.x = max(maxScroll.cx - (INT)rect.getWidth(), 0);
    scroll.max.y = max(maxScroll.cy - (INT)rect.getHeight(), 0);

    // set scroll values
    scroll.pos.x = min(scroll.max.x, scroll.pos.x);
    scroll.pos.y = min(scroll.max.y, scroll.pos.y);
  }

  // initialize scroll info
  SCROLLINFO info;
  info.cbSize = sizeof(info);
  info.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  info.nMin = 0;

  // scroll surface horizontally
  info.nMax  = maxScroll.cx;
  info.nPage = rect.getWidth();
  info.nPos = scroll.pos.x;
  SetScrollInfo(*this, SB_HORZ, &info, TRUE);

  // scroll surface vertically
  info.nMax  = maxScroll.cy;
  info.nPage = rect.getHeight();
  info.nPos = scroll.pos.y;
  SetScrollInfo(*this, SB_VERT, &info, TRUE);

  invalidate(FALSE);
}

VOID    WrapWnd::scrollToPoint(const POINT& point, ScrollInfo& scroll)
{
  Geom::Size client = Geom::Size::fromWindow(*this);

  // scroll to mouse vertical pos
  if (point.y < 0)
  {
    setScrollY(scroll.pos.y + point.y, scroll);
  }
  else if (point.y > client.cy)
  {
    setScrollY(scroll.pos.y + (point.y - client.cy), scroll);
  }

  // scroll to mouse horizontal pos
  if (point.x < 0)
  {
    setScrollX(scroll.pos.x + point.x, scroll);
  }
  else if (point.x > client.cx)
  {
    setScrollX(scroll.pos.x + (point.x - client.cx), scroll);
  }
}

VOID    WrapWnd::scrollPoint(POINT& point, ScrollInfo& scroll)
{
  Geom::Size content = Geom::Size::fromWindow(*this) + Size(scroll.max);

  point.x = max(point.x, 0) + scroll.pos.x;
  point.x = min(point.x, (INT)content.cx);
  point.y = max(point.y, 0) + scroll.pos.y;
  point.y = min(point.y, (INT)content.cy);
}
#pragma endregion
